using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Universe.Farm
{
    [Serializable]
    public class DescriptionBuffer
    {
        public enum SerializerKind: byte
        {
            BinarySerializer,
            XmlSerializer
        }

        SerializerKind _Kind;
        byte[] _Raw;
        private object _Value;
        private bool IsParsed = false;
        object Sync = new object();


        public DescriptionBuffer()
        {
        }


        public SerializerKind Kind
        {
            get { return _Kind; }
        }


        public byte[] Raw
        {
            get { return _Raw; }
        }

        public static DescriptionBuffer Create(Type type, object instance)
        {
            return Create(type, instance, SerializerKind.XmlSerializer);
        }

        public static DescriptionBuffer Create(Type type, object instance, SerializerKind kind)
        {
            byte[] raw = null;
            if (null != instance)
            {
                switch (kind)
                {
                    case SerializerKind.BinarySerializer:
                        BinaryFormatter bf = new BinaryFormatter();
                        using (MemoryStream mem = new MemoryStream())
                        {
                            bf.Serialize(mem, instance);
                            raw = mem.ToArray();
                        }

                        break;

                    case SerializerKind.XmlSerializer:
                        XmlSerializer xs = new XmlSerializer(type);
                        using (MemoryStream mem = new MemoryStream())
                        using (StreamWriter wr = new StreamWriter(mem, Encoding.UTF8))
                        {
                            XmlTextWriter xw = new XmlTextWriter(wr);
                            xw.Formatting = Formatting.None;
                            xs.Serialize(xw, instance);
                            xw.Flush();
                            wr.Flush();
                            raw = mem.ToArray();
                        }

                        break;


                    default:
                        throw new NotSupportedException();
                }
            }

            return new DescriptionBuffer(kind, raw);
        }


        public DescriptionBuffer(SerializerKind kind, byte[] raw)
        {
            _Kind = kind;
            _Raw = raw;
        }

        public T Get<T>()
        {
            if (!IsParsed)
                lock (Sync)
                    if (!IsParsed)
                    {
                        if (_Raw != null)
                            _Value = Read(typeof (T));

                        IsParsed = true;
                    }

            return _Value == null ? default(T) : (T) _Value;
        }

        private object Read(Type type)
        {
            switch (_Kind)
            {
                case SerializerKind.BinarySerializer:
                    BinaryFormatter bf = new BinaryFormatter();
                    using (MemoryStream mem = new MemoryStream(_Raw))
                    {
                        object raw = bf.Deserialize(mem);
                        return raw;
                    }

                case SerializerKind.XmlSerializer:
                    XmlSerializer xs = new XmlSerializer(type);
                    using(MemoryStream mem = new MemoryStream(_Raw))
                    using(StreamReader rd = new StreamReader(mem, Encoding.UTF8))
                    {
                        XmlTextReader xr = new XmlTextReader(rd);
                        object raw = xs.Deserialize(xr);
                        return raw;
                    }

                default:
                    throw new NotSupportedException("Serialization Kind " + _Kind + " is not supported");
            }
        }
    }
}